#include "Link.h"
#include "XMLTree.h"
#include "HBSD.h"
#include "Node.h"
#include <iostream>
#include "Policy.h"
#include "Links.h"
#include "Util.h"


using namespace std;
int Link::action;

Link::Link(Links *links) 
{
	thrdState = STATE_NONEXTANT;
	canAttach = false;
	openRequested = false;
	threadRun = false;
	thread = 0;
	type = TYPE_OTHER;
	state = STATE_NONEXTANT;
	maxSendsOutstanding = DEFAULT_MAX_OUTSTANDING_SENDS;
	linkMaxSendsOutstanding = DEFAULT_MAX_OUTSTANDING_SENDS;
	contactAttrs = NULL;

	// State should be taken from the state variables rather then this element.

	linkAttrs = NULL;
	clInfo = NULL;
	action = ACTION_NONE;

	linkManager = links;
	sem_init(&synSem, 0, 1);
	sem_init(&queueSem, 0, 1);
}

Link::~Link()
{

	while(!msgQueue.empty())
	{
		delete msgQueue.front();
		msgQueue.pop();
	}
	while(!syncQ.empty())
	{
		syncQ.pop();
	}

	sem_destroy(&synSem);

	sem_destroy(&queueSem);

}

void* Link::run(void * arg) 
{
	threadParam * tp = (threadParam *)arg;

	while (true) 
	{
		// Wait for a message to arrive.
		LinkMessage* msg = NULL;
		try 
		{		
			
			msg = tp->currentLink->msgQueueTake();

			// Have we been asked to stop the thread?
			if (tp->currentLink->action == tp->currentLink->ACTION_STOP) 
			{
				tp->currentLink->finish();
				free(tp);
				return NULL;
			}
			if (msg != NULL) 
			{
				// It doesn't matter what type of message woke us up, the
				// routine is the same: determine the state of the link,
				// see if we can act on any bundles. It's safe to delete all
				// messages from the msgQueue: the purpose of all messages
				// is to wake this thread up, not for the thread to act on
				// a specific message type.
				switch (msg->type) 
				{
					case LinkMessage::MSG_BUNDLE:
					case LinkMessage::MSG_WAKEUP:
					case LinkMessage::MSG_STATE:
					case LinkMessage::MSG_TRANSMIT:
					case LinkMessage::MSG_ATTACH:
						if (HBSD::log->enabled(Logging::DEBUG)) 
						{
							HBSD::log->debug(string("Link thread was woken up: ") + tp->currentLink->id);
							if(tp->currentLink->thrdCurrentNode == NULL) 
							{
								HBSD::log->debug(string("Link ") + tp->currentLink->id + string(" is not associated with a node"));
							} else 
							{
								HBSD::log->debug(string("Link ") + tp->currentLink->id + string(" is associated with a node: ") + tp->currentLink->thrdCurrentNode->eid);
							}
						}
						tp->currentLink->finish();
						tp->currentLink->takeAction();
						break;
					default:
						if (HBSD::log->enabled(Logging::ERROR)) 
						{
							HBSD::log->error(string("Unknown LinkMessage type dequeued"));
						}
						break;
				}
			}
		} 

		/*catch (InterruptedException& e) 
		{
			// Do nothing except wait for the next message.
		} */
		catch (exception& e) 
		{
			if (HBSD::log->enabled(Logging::ERROR)) 
			{
				HBSD::log->error(string("Unanticipated exception in Link thread: ") + string(e.what()));
			}
		}
		// Have we been asked to stop the thread?
		if (tp->currentLink->action == ACTION_STOP) 
		{
			tp->currentLink->finish();
			free(tp);
			return NULL;
		}
			
		
	}
	if(tp != NULL)
		free(tp);
}
	
	
void Link::available(XMLTree *element) 
{
	if (!threadRun) 
	{
		startThread();
	}

	openRequested = false;

	if (state == STATE_OPEN) 
	{
		return;
	}
	if (state == STATE_BUSY) 	
	{
		stateChange(STATE_OPEN);
	} else 
	{
		stateChange(STATE_AVAILABLE);
	}
}


void Link::opened(XMLTree *element) 
{
	openRequested = false;
	if (state >= STATE_OPEN) 
	{
		if (state == STATE_BUSY) 
		{
			stateChange(STATE_OPEN);
		}
		return;
	}
	// The following sets the remoteEID.
	findAndSaveAttributesContact(element);
	// Associate the destination node with this link. It's possible that
	// the node is already associated with another link, which can
	// happen due to opportunistic links. If that's the case we let the
	// link remain open with no associated node. We do have logic
	// elsewhere that allows a node to attach later on.
	Node* openedNode = linkManager->router->nodes->conditionalAdd(remoteEID);
	if (!openedNode->setLink(this)) 
	{
		openedNode = NULL;
	}
	currentNode = openedNode;
	if (!threadRun) 
	{
		startThread();
	}
	/*
		* Once we change the state to OPEN the thread can re-read values
		* about the link. At that point the thread makes its own copy of
		* values that it will use. We wait until the thread tells us that
		* it is aware of the state change.
		*/
	stateChange(STATE_OPEN);
}

void Link::closed(XMLTree *element) 
{
	findAndSaveAttributesContact(element);
	if (thread == 0) 
	{
		startThread();
	}
	openRequested = false;
	stateChange(STATE_CLOSED);
}


void Link::busy(XMLTree *element) 
{

	openRequested = false;
	if (state != STATE_OPEN) 
	{
		return;
	}
	stateChange(STATE_BUSY);
}

void Link::dataTransmitted(long id, int bytesSent, int bytesReliable) 
{
	linkManager->router->policyMgr->bundleTransmitStop(this, currentNode,id, bytesSent, bytesReliable);
	sem_wait(&queueSem);
	msgQueue.push(new LinkMessage(LinkMessage::MSG_STATE));
	sem_post(&queueSem);
}


bool Link::associate(Node *node, bool switching) 
{
	if (HBSD::log->enabled(Logging::DEBUG)) 
	{
		if (switching)
		{
			HBSD::log->debug(string("associate() has been called for new node: ") + node->eid + string("(switching=true)"));
		} else 
		{
			HBSD::log->debug(string("associate() has been called for new node: ")	+ node->eid + string(" (switching=false)"));
		}
	}
	// Link must be open to the node!
	if ((state < STATE_OPEN) || (remoteEID.compare(node->eid) != 0)) 
	{
		return false;
	}
	// This link is open to the node so we can begin using the link.
	// We synchronize this action with the link thread:
	//   - assign the node object to a global variable
	//   - set a bool indicating a node is available
	//   - have the node associate with this link
	//   - wake up the link thread
	//   - tell the policy manager about the switch if switching
	//   - wait for a response from the link thread
	//   - clear variables
	//   - tell the policy manager about the new association if
	//     switched is not set
	attachNode = node;
	canAttach = true;
	if (switching) 
	{
		node->setLink(this);
		linkManager->router->policyMgr->linkSwitched(this, node);
	}

	sem_wait(&queueSem);
	msgQueue.push(new LinkMessage(LinkMessage::MSG_ATTACH));
	sem_post(&queueSem);

	while (true) 
	{
		try 
		{
			sem_wait(&synSem);
			syncQ.pop();
			sem_post(&synSem);
			break;
		} 
		catch (exception& e) 
		{
		}
	}

	canAttach = false;
	attachNode = NULL;

	if (!switching) 
	{
		linkManager->router->policyMgr->linkAssociated(this, node);
	}

	return true;
}



void Link::conditionalUse(Node *node) 
{
	if (HBSD::log->enabled(Logging::DEBUG)) 
	{
		HBSD::log->debug(string("conditionalUse() called for link ") + id + string(" by EID ") + node->eid);
	}
	if (currentNode != NULL) 
	{
		// Already servicing another Node.
		return;
	}
	if (associate(node, false)) 
	{
		// This shouldn't normally occur; associate() should be
		// called when the previously associated link closes.
		return;
	}
	// Is there already an open request to DTN pending?
	if (openRequested) 
	{
		return;
	}
	// Allow the policy manager to decide whether to open the link.
	if (linkManager->router->policyMgr->requestLinkOpen(this, node)) 
	{
		// Make a request to DTN that the link be opened. Mark that
		// we have a request outstanding (the value gets set to false
		// whenever we receive a link event).
		HBSD::requester->requestOpenLink(id);
		openRequested = true;
	}
}



void Link::requestOpen() 
{
	// Do we already have an open request pending?
	if (openRequested) 
	{
		return;
	}
	// Only states we assume we can request an open from.
	if ((state != STATE_AVAILABLE) && (state != STATE_CLOSED))
	{
		return;
	}
	// Only types we assume we request an open for.
	if ((type != TYPE_ALWAYSON) && (type != TYPE_ONDEMAND)) 
	{
		return;
	}
	// Send a request to open the link to DTN and mark the
	// link as having a request outstanding. We expect DTN to
	// eventually respond with something, e.g. this link is
	// unavailable or open, and we mark the request as not being
	// outstanding whenever we hear something.
	HBSD::requester->requestOpenLink(id);
	openRequested = true;
}

bool Link::init(XMLTree *evtLinkCreated) 
{
	// There should be an id, i.e. the link should be named.
	if (evtLinkCreated->haveAttr(string("link_id"))) 
	{
		id = evtLinkCreated->getAttr(string("link_id"));
	} else 
	{
		return false;
	}
	// One child element should be the link attributes (link_attr).
	// Find it and get the attributes that we initially care about.
	// Save the entire element for possible future use.
	try 
	{
		XMLTree* el = evtLinkCreated->getChildElementRequired(string("link_attr"));
		if (!parseLinkAttr(el)) 
		{
			return false;
		}
		storeLinkElement(el);
		findAndSaveAttributesCLinfo(el);
		if (state >= STATE_AVAILABLE)
		{
			startThread();
		}
		return true;
	} 
	catch (exception& e) 
	{
		return false;
	}
}

bool Link::parseLinkAttr(XMLTree *element) 
{
	try 
	{
		string str = element->getAttrRequired(string("type"));
		type = whatType(str);
		str = element->getAttrRequired(string("state"));
		state = whatState(str);
		return true;
	} 
	catch (exception & e) 
	{
		return false;
	}
}

void Link::stateChange(int newState) 
{
	if (newState == state) 
	{
		return;
	}

	Node *prevNode = currentNode;
	bool sync = false;
	bool toOpenFromClosed = false;
	if ((newState >= STATE_OPEN) && (state < STATE_OPEN)) 
	{

		// Non-open to open.
		sync = true;
		toOpenFromClosed = true;
		// Create a temporal route.
		linkManager->router->routes->addTemporalRoute(remoteEID, this);
		// Tell the policy manager about the link opening before
		// telling the thread.
		linkManager->router->policyMgr->linkOpened(this, currentNode);

	} else if ((state >= STATE_OPEN) && (newState <STATE_OPEN)) 
	{
		// Open to non-open.
		sync = true;
		toOpenFromClosed = false;
		// Un-associate link and node.
		if (currentNode != NULL) 
		{
			currentNode->clearLink();
			currentNode = NULL;
		}
		// Delete the temporal route.
		linkManager->router->routes->deleteTemporalRoute(remoteEID, this);
	}
	if (HBSD::log->enabled(Logging::DEBUG)) 
	{
		if (sync) 
		{
			HBSD::log->debug(string("Notifying link thread of synchronizing state change: ") + Util::to_string(newState));
		} else 
		{
			HBSD::log->debug(string("Notifying link thread of non-synchronizing state change: ") + Util::to_string(newState));
		}
	}
	// Notify the Link thread of the state change. If the state change
	// requires synchronization with the thread, wait.
	state = newState;

	sem_wait(&queueSem);
	msgQueue.push(new LinkMessage(LinkMessage::MSG_STATE));
	sem_post(&queueSem);

	while (sync) 
	{
		try 
		{
			sem_wait(&synSem);
			syncQ.pop();
			sem_post(&synSem);
			break;
		} 
		catch (exception& e) 
		{
			
		}
	}
	if (sync) 
	{
		if (HBSD::log->enabled(Logging::DEBUG)) 
		{
			HBSD::log->debug(string("Main thread finished synchronization"));
		}
	}
	// Have we closed a previously open link?
	if (sync && !toOpenFromClosed) 
	{
		bool switched = false;
		if (prevNode != NULL) 
		{
			// We went from an open to a non-open link. Is there another
			// link that the node can use?
			Link* newLink = linkManager->router->routes->getTemporalLink(prevNode->eid, this);
			if (newLink != NULL) 
			{
				switched = newLink->associate(prevNode, true);
			}
			if (!switched) 
			{
				// We were unable to switch to another link. Let the
				// policy manager know about the link closing. If we did
				// switch, the policy manager will be informed by the
				// new link's associate() method.
				linkManager->router->policyMgr->linkNotOpen(this, prevNode);
			} else 
			{
				// We did switch and the policy manager has been informed.
				// We still need to tell the policy manager that this
				// link is no longer open.
				linkManager->router->policyMgr->linkNotOpen(this, NULL);
			}
		}
	}
}
	

void Link::takeAction() 
{
	// We maintain a worst-case count of sends outstanding so that we don't
	// have to query for the actual value with each loop iteration.
	maxSendsOutstanding = linkManager->router->policyMgr->maxOutstandingTransmits(this, thrdCurrentNode, metaDataSent, linkMaxSendsOutstanding);
	int currentSends = maxSendsOutstanding;
	while (true) 
	{
		// Determine the perceived state of the link.
		if (checkState()) 
		{
			currentSends = maxSendsOutstanding;
		}
		// If the link is busy, stop.
		if (thrdState == STATE_BUSY) 
		{
			return;
		}
		// Is the link associated with a node?
		if (thrdCurrentNode == NULL) 
		{
			// Not associated, but can we associate?
			if (canAttach) 
			{
				attachToNode();
				// Re-check state, we may or may net be associated.
				continue;
			}
			return;
		}
		// If the link is not open then we cannot use it. Request to open links
		// is handled elsewhere.
		if (thrdState < STATE_OPEN) 
		{
			return;
		}
		// Make certain we don't already have the maximum number of sends
		// outstanding.
		if (currentSends >= maxSendsOutstanding) 
		{
			maxSendsOutstanding =	linkManager->router->policyMgr->maxOutstandingTransmits(this,thrdCurrentNode, metaDataSent, linkMaxSendsOutstanding);
			currentSends = linkManager->router->policyMgr->outstandingTransmits(this, thrdCurrentNode);
			if (currentSends >= maxSendsOutstanding) 		
			{
				if (HBSD::log->enabled(Logging::DEBUG)) 
				{
					HBSD::log->debug(string("Link is flow controlled: ") + id + string(" (eid=") + thrdCurrentNode->eid + string(")"));
				}
				return;
			}
		}
		// Optimistically update the count of outstanding sends. This just
		// controls how often we query the Policy Manager for the real
		// count.
		currentSends++;
		// The order of sending is: meta data, bundles destined for the peer,
		// data to be replicated at the peer. However, the Policy Manager can
		// play any games with the queues that it wants.
		if (!sendBundle(Policy::QUEUE_METADATA)) 
		{
			// Have we not yet sent meta data and, if not, are we supposed
			// to wait before sending any other bundles?
			if (!metaDataSent && (waitPolicy <= Policy::WAIT_METATDATA)) 
			{
				if (HBSD::log->enabled(Logging::DEBUG)) 
				{
					HBSD::log->debug(string("Waiting for meta data to be injected"));
				}
				currentSends--;
				return;
			}
			// Try sending a bundle.
			if (!sendBundle(Policy::QUEUE_DELIVERY)) 
			{
				if (!metaDataSent && (waitPolicy <= Policy::WAIT_DELIVERY)) 
				{
					if (HBSD::log->enabled(Logging::DEBUG)) 
					{
						HBSD::log->debug(string("Waiting for delivery queue or meta data to be injected"));
					}
					currentSends--;
					return;
				}
				if (!sendBundle(Policy::QUEUE_REPLICA)) 
				{
					currentSends--;
					if (HBSD::log->enabled(Logging::DEBUG)) 
					{
						HBSD::log->debug(string("No bundles to be sent by link: ") + id);
					}
					return;
				}
			}
		}
	}
}
	
bool Link::sendBundle(int queue) 
{
	Bundle* bundle;
	bool toDest;
	bool metaData;
	do  
	{
		bundle = NULL;
		toDest = false;
		metaData = false;
		// We ask the Node object for the bundle. (And the Node object
		// asks the Policy Manager.)
		switch (queue) 
		{
			case Policy::QUEUE_METADATA:
				bundle = thrdCurrentNode->metadataPoll();
				if (bundle != NULL)
				{
					metaDataSent = true;
				}
				metaData = true;
				toDest = true;
				break;
			case Policy::QUEUE_DELIVERY:
				bundle = thrdCurrentNode->deliveryPoll();
				toDest = true;
				break;
			case Policy::QUEUE_REPLICA:
				bundle = thrdCurrentNode->replicaPoll();
				break;
			default:
				return false;
		}
		if (bundle == NULL) 
		{
			return false;
		}
		// Though the Policy Manager ultimately gave us the bundle, we
		// provide an opportunity for the Policy Manager to veto sending
		// the bundle. This is because the Policy Manager may know that
		// a bundle shouldn't be sent (e.g., it is known to have been
		// delivered), yet the bundle still existed in the Node's
		// transmission queue. If the Policy Manager vetoes the bundle,
		// try to get another bundle.
		if (linkManager->router->policyMgr->shouldTransmit(this, bundle, thrdCurrentNode, queue)) 
		{
			break;
		}
		if (HBSD::log->enabled(Logging::DEBUG)) 
		{
			HBSD::log->debug(string("Link was told not to send a bundle"));
		}
	} while (true);

	// Tell the Policy Manager before sending to ensure it knows about the
	// transmit before the completion.
	linkManager->router->policyMgr->bundleTransmitStart(this, thrdCurrentNode, bundle, toDest, metaData, queue);
	HBSD::requester->requestSendBundle(bundle, id, Requester::FWD_ACTION_COPY);
	return true;
}



bool Link::checkState() 
{
	// Read the state value. The read is an atomic operation.
	// Note that once we've read the state it can change again,
	// but that's permissible since we will check again.
	int newState = state;
	if (newState == thrdState) 
	{
		// No change
		return false;
	}

	int prevState = thrdState;
	// Have we had a change to/from the OPEN state? If so, then
	// we need to synchronize.

	bool sync;

	if (((newState >= STATE_OPEN) && (prevState < STATE_OPEN)) ||
	((prevState >= STATE_OPEN) && (newState <STATE_OPEN))) 
	{
		sync = true;
	} else 
	{
		sync = false;
	}
	// If the link is now open get the node (if any) that this link
	// is associated with. Also, indicate that we have yet to send
	// meta data and determine what to do if there is as yet no
	// meta data to be sent. Note that we can be going from busy
	// to open, in which case nothing has changed except that we
	// can start sending again.
	if (newState >= STATE_OPEN) 
	{
		if ((newState == STATE_OPEN) && (prevState < STATE_OPEN)) 
		{
			thrdCurrentNode = currentNode;
			metaDataSent = false;
			if (thrdCurrentNode != NULL) 
			{
				waitPolicy = linkManager->router->policyMgr->waitPolicy(this, thrdCurrentNode);
			}
		}
	} else 
	{
		// We may be closing and no longer attached to a node.
		if (sync) 
		{ 
			thrdCurrentNode = currentNode;
		}
	}

	thrdState = newState;

	if (sync) 
	{
		if (HBSD::log->enabled(Logging::DEBUG)) 
		{
			string s;
			if (newState >= STATE_OPEN) 
			{
				s.assign("to open");
			} else 
			{
				s.assign("to closed");
			}
			HBSD::log->debug(string("Link thread aware of state transition: ") + s);
		}

		// Synchronize with the main thread.
		try 
		{
			sem_wait(&synSem);
			syncQ.push(0);
			sem_post(&synSem);
		} 
		catch (exception& e) 
		{
		}
	}
	if (HBSD::log->enabled(Logging::DEBUG)) 
	{
		if (sync) 
		{
			HBSD::log->debug(string("Link thread finished synchronization"));
		}
		if (thrdCurrentNode == NULL) 
		{
			HBSD::log->debug(string("Link ") + id + string(" not associated with a node"));
		} else 
		{
			HBSD::log->debug(string("Link ") + id + string(" associated with node: ") + thrdCurrentNode->eid);
		}
	}
	return true;
}

void Link::attachToNode() 
{
	if (HBSD::log->enabled(Logging::DEBUG)) 
	{
		HBSD::log->debug(string("Attempting to complete an association"));
	}
	thrdCurrentNode = attachNode;
	canAttach = false;
	try 
	{
		sem_wait(&synSem);
		syncQ.push(0);
		sem_post(&synSem);
	} 
	catch (exception& e) 
	{	
	}
	if (HBSD::log->enabled(Logging::DEBUG)) 
	{
		if (thrdCurrentNode != NULL) 
		{
			HBSD::log->debug(string("Link ") + id + string(" associated with node: ") + thrdCurrentNode->eid);
		}
	}
	if (!metaDataSent) 
	{
		waitPolicy = linkManager->router->policyMgr->waitPolicy(this, thrdCurrentNode);
	} else 
	{
		waitPolicy = Policy::WAIT_ANY;
	}
}


void Link::startThread()
{
	thrdState = state;
	action = ACTION_RUN;
	pthread_attr_t threadAttr;

	threadParam * ptrParam = (threadParam *)malloc(sizeof(threadParam));
	ptrParam->currentLink = this;

	if (pthread_attr_init (&threadAttr) != 0) 
	{
		cerr<<"pthread_attr_init error."<<endl;
		exit(1);
	}

	if (pthread_attr_setdetachstate (&threadAttr,PTHREAD_CREATE_DETACHED) != 0)
	{
		cerr<<"pthread_attr_setdetachstate error."<<endl;
		exit(1);
	}
		
	if (pthread_create (&thread, &threadAttr, run, (void *)ptrParam) < 0) 
	{
		threadRun = false;
		cerr<<"pthread_create error."<<endl;
	}
	threadRun = true;
	ptrParam = NULL;
}

void Link::findAndSaveAttributesContact(XMLTree *event) 
{
	try 
	{
		XMLTree* el = event->getChildElementRequired(string("contact_attr"));
		storeContactElement(el);
		findAndSaveAttributesLink(el);
	} 
	catch (exception& e) 
	{
		cerr<<"Exception occured in Link::findAndSaveAttributesContact: "<<e.what()<<endl;
	}
}


void Link::findAndSaveAttributesLink(XMLTree *event) 
{
	try 
	{
		XMLTree* el = event->getChildElementRequired(string("link_attr"));
		storeLinkElement(el);
		findAndSaveAttributesCLinfo(el);
		findAndSaveAttributesRemoteEID(el);
	} 
	catch (exception& e) 
	{
		cerr<<"Exception occured in Link::findAndSaveAttributesLink: "<<e.what()<<endl;
	}
}


void Link::findAndSaveAttributesCLinfo(XMLTree *event) 
{
	try 
	{
		XMLTree* el = event->getChildElementRequired(string("clinfo"));
		storeCLinfoElement(el);
		maxSendsOutstanding = DEFAULT_MAX_OUTSTANDING_SENDS;
		linkMaxSendsOutstanding = DEFAULT_MAX_OUTSTANDING_SENDS;
		maxSendsOutstanding = atoi(el->getAttrRequired(string("busy_queue_depth")).c_str());
		linkMaxSendsOutstanding = maxSendsOutstanding;
		remoteAddr = el->getAttrRequired(string("remote_addr"));
	} 
	catch (exception & e)
	{
		
	}
}

void Link::findAndSaveAttributesRemoteEID(XMLTree *event) 
{
	try 
	{
		XMLTree* el = event->getChildElementRequired(string("remote_eid"));
		string uri = el->getAttrRequired(string("uri"));
		if (remoteEID.empty()) 
		{
			remoteEID = uri;
		} else if (uri.compare(remoteEID) != 0) 
		{
			remoteEID = uri;
		}
	} 
	catch (exception & e) 
	{
		cerr<<"Exception occured in Link::findAndSaveAttributesRemoteEID: "<<e.what();
	}
}



int Link::whatState(string str) 
{
	if (str.compare("available") == 0) 
	{
		return STATE_AVAILABLE;
	}
	if (str.compare("open") == 0) 
	{
		return STATE_OPEN;
	}
	if (str.compare("opening") == 0) 
	{
		return STATE_OPENING;
	}
	if (str.compare("busy") == 0) 
	{
		return STATE_BUSY;
	}
	return STATE_UNAVAILABLE;
}

int Link::whatType(string str) 
{
	if (str.compare("alwayson") == 0) 
	{
		return TYPE_ALWAYSON;
	}
	if (str.compare("ondemand") == 0) 
	{
		return TYPE_ONDEMAND;
	}
	if (str.compare("scheduled") == 0) 
	{
		return TYPE_SCHEDULED;
	}
	if (str.compare("opportunistic") == 0) 
	{
		return TYPE_OPPORTUNISTIC;
	}
	return TYPE_OTHER;
}
